Skip to content

fix(test): retain file ownership through isolation teardown - #162

Merged
vincentkoc merged 3 commits into
mainfrom
claude/fix-test-teardown-accounting-20261014
Oct 14, 2026
Merged

vincentkoc merged 3 commits into
mainfrom
claude/fix-test-teardown-accounting-20261014

Conversation

@vincentkoc

@vincentkoc vincentkoc commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

What does this PR do?

An isolated test file released its active-file and reporter scope before closing live handles and swapping its global. A user close callback could then throw without an owning test file: the run could remain successful and JUnit could omit the failure.

The canonical file runner now owns handle shutdown and the global swap before releasing the outgoing file's test/reporting scope. begin_file_teardown() enters the terminal phase and cancels the active timer before callbacks run, so pending collection hooks cannot resume during finalization. Existing cleanup callbacks still run in their original order. Errors that reach the uncaught hook intentionally fail the run and are attributed once to the outgoing file through the existing serial/JUnit path or worker TestDone payload. JUnit file finalization follows cleanup. The duplicated caller-level stop/swap paths are removed.

The accompanying network tests repair the leaks and races that exposed this boundary:

  • HTTP timeout tests own both requests until their explicit destruction, error delivery, and close events settle; the server close callback completes the test. The connected-request sibling checks its expected ECONNRESET instead of swallowing it.
  • Controlled lookup completion and a local pending TCP connection replace WAN-dependent abort races. Genuine Node 26.3.0 and Bun exercise the portable pending-abort cases with both address-family settings. Bun's already-aborted immediate-connect contract has separate coverage; Node only guarantees reconnecting after close.

Raw production delta is +81 lines (+115/-34), or +82 excluding one trailing blank-line removal. This adds the file-lifetime ownership and structured error-reporting boundary. Tests are separate: +349 net lines (+448/-99); changelog +2.

The narrow synthetic file-error reporting pattern adapts oven-sh#44435. The native commit retains Co-authored-by: robobun <117481402+robobun@users.noreply.github.com> and the changelog credit.

There is a policy overlap with oven-sh#44841: this fork intentionally tightens uncaught-error semantics during isolated cleanup while preserving callback delivery. An upstream submission needs explicit design review of that policy; this PR does not import either broader upstream change.

Deferred pre-existing follow-up: RefData debug formatting can dereference a released collection scope. This is a debug-only source finding, with no runtime invalid-read reproduction. This PR does not change the logger; it remains a separate investigation.

How did you verify your code works?

Exact signed native-proof head: cd175f6191d04fa5438925d69a3be557b8743576, based on f3f6d6c33bd2a762e530cef167d04a5ff872cc96. Linux ARM64 release/LTO built through bun bd --profile=release --lto=on --ci=on -j2, with CI, GITHUB_ACTIONS, and BUILDKITE unset so trailing test arguments execute. The fork's pinned published engine was used. Native Bun.revision matches that tested head, and all 20,075 tracked source files matched its tree during qualification.

Current PR head 42c73e9090d6a0c6f511904d360c43df4c9f1909 appends only a relocation of the existing SAFETY comment immediately above its unsafe operand, fixing Clippy's undocumented_unsafe_blocks finding. The runtime code and tests are unchanged. Pinned Rust formatting passes; all nine named hosted checks pass on this head. The native results below belong to cd175f6191d04fa5438925d69a3be557b8743576 and were not rerun for this comment-only change.

Binary SHA256: 506da05105a1200439144e074adce63d65c021f0a0ac9a81a0b3e7302c0aeeab.

Qualification Observed result
Preserved exact-base counterfactual, eight new isolation cases Three handled-cleanup controls pass; five cases fail for the missing failure attribution/reporting. Failed-load fixtures reach their pending hook and close callback.
Candidate test/cli/test/isolation.test.ts 59 pass, including all eight new cases: serial non-last file, real parallel execution, worker-last file, handled callbacks, and early failed-load teardown.
Whole HTTP request group 25 pass.
Net abort matrix, pending native abort, shutdown and reconnect controls 10 pass. Portable cases execute under genuine Node 26.3.0 as well as Bun.
Parallel suite 44 pass and one skip. The remaining SIGTERM case initially failed because the container's sleeping PID1 did not reap terminated children.
Exact SIGTERM case after environment correction Pass under distro tini 0.19.0 -s. The same zombie condition was reproduced on the preserved baseline; source and assertions were unchanged. Default child exit-code passthrough was verified.
Rerun, concurrent, JUnit and selected watch controls 31 pass under the same subreaper. Previously passing suites were not repeated.
Original 16-file raw combined batch Original order, width 3, 90-second per-test ceiling, zero retries: 1,226 pass, 16 skip, one todo, zero fail. Exit 0; JUnit 1,243 cases, zero failures/errors.

The original combined batch has zero GitHub ::error annotations and zero unhandled-error reports. A handled TLS SocketProxy ECONNRESET diagnostic remains; this is not a claim that stderr contains no error-related text.

Scoped canonical formatting and final cargo fmt --all -- --check --verbose pass. Native ownership and fixture reviews are clear; scoped AutoReview found no actionable findings, and the final net runtime-selection change received separate review.

The full frozen test-dependency installation did not complete. The selected dependency closure was verified, and every requested test above executed successfully. This is selected Linux ARM64 qualification, not a full cross-platform suite claim; hosted checks at 42c73e9 pass: Linux x64, macOS ARM64, Clippy, Miri, lol-html, mordant, JavaScript lint, source lints, and Format (https://github.com/openclaw/bun/actions/runs/38499206131).

vincentkoc and others added 3 commits October 14, 2026 09:02
Wait for request and server cleanup, assert timeout notification and socket error ordering, and replace WAN abort races with held lookup and local pending-connection fixtures. Keep pending-abort parity with Node separate from Bun pre-aborted immediate-connect behavior.
Finalize file-owned handles and globals before releasing the active test owner and JUnit reporter. Mark teardown terminal without discarding callback metadata, and report uncaught cleanup errors through the existing file failure and worker IPC paths.

Adapt the structured unhandled-error reporting pattern from oven-sh#44435 while preserving the existing cleanup callback policy.

Co-authored-by: robobun <117481402+robobun@users.noreply.github.com>
@vincentkoc
vincentkoc marked this pull request as ready for review October 14, 2026 02:27
@vincentkoc
vincentkoc merged commit 34fc20d into main Oct 14, 2026
10 checks passed
@vincentkoc
vincentkoc deleted the claude/fix-test-teardown-accounting-20261014 branch October 14, 2026 02:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant